package ar.uba.fi.RSACryptoFiuba.view.keygeneration;

import java.awt.CardLayout;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.Observable;
import java.util.Observer;

import javax.swing.GroupLayout;
import javax.swing.GroupLayout.Alignment;
import javax.swing.JPanel;
import javax.swing.JSeparator;
import javax.swing.LayoutStyle.ComponentPlacement;

import ar.uba.fi.RSACryptoFiuba.model.InvalidValueException;
import ar.uba.fi.RSACryptoFiuba.model.RSAAlgorithmEvaluationStrategy;
import ar.uba.fi.RSACryptoFiuba.model.RSAAlgorithmLearningStrategy;
import ar.uba.fi.RSACryptoFiuba.model.keygeneration.RSAKeyGenerationAlgorithm;
import ar.uba.fi.RSACryptoFiuba.model.keygeneration.RSAKeyGenerationMessage;
import ar.uba.fi.RSACryptoFiuba.view.commons.ButtonsPanel;
import ar.uba.fi.RSACryptoFiuba.view.commons.EvaluationInteractionPanel;
import ar.uba.fi.RSACryptoFiuba.view.commons.ResultsPanel;


@SuppressWarnings("serial")
public class GenerateKeysEvaluationPanel extends JPanel implements Observer {
	
	private GenerateKeysProgressPanel progressPanel;
	
	private JPanel cards;
    private CardLayout cardsLayout;
    private EvaluationInteractionPanel generatePPanel;
    private EvaluationInteractionPanel generateQPanel;
    private EvaluationInteractionPanel calculateNPanel;
    private EvaluationInteractionPanel calculatePhiPanel;
    private EvaluationInteractionPanel calculateDPanel;
    private EvaluationInteractionPanel calculateEPanel;
    private ResultsPanel generateKeysResultsPanel;
    
    private JSeparator lineSeparator;
    private ButtonsPanel buttonsPanel;
    
    private RSAKeyGenerationAlgorithm algorithm;
    private RSAKeyGenerationMessage message;
	private RSAAlgorithmEvaluationStrategy evaluationStrategy;
	private RSAAlgorithmLearningStrategy learningStrategy;
    	
	public GenerateKeysEvaluationPanel(ActionListener modeButtonActionListener, RSAKeyGenerationMessage keyGenerationMessage){
		this.message = keyGenerationMessage;
		this.evaluationStrategy = new RSAAlgorithmEvaluationStrategy();
		this.learningStrategy = new RSAAlgorithmLearningStrategy();
		this.algorithm = new RSAKeyGenerationAlgorithm(evaluationStrategy, message);
		this.algorithm.getMessage().addObserver(this);
		initComponentes(modeButtonActionListener);
		handleButtonsEnable();
	}
	
	private void initComponentes(ActionListener modeButtonActionListener){		
		
		//Progress Panel
        progressPanel = new GenerateKeysProgressPanel();
                        
        //Step Panels
        cards = new JPanel();
        cards.setPreferredSize(new Dimension(670, 165));
        
        cardsLayout = new CardLayout();
        cards.setLayout(cardsLayout);
        
        generatePPanel = new EvaluationInteractionPanel("p:");
        cards.add(generatePPanel, "GENERATE_P");
        generateQPanel = new EvaluationInteractionPanel("q:");
        cards.add(generateQPanel, "GENERATE_Q");
        calculateNPanel = new EvaluationInteractionPanel("n:");
        cards.add(calculateNPanel, "CALCULATE_N");
        calculatePhiPanel = new EvaluationInteractionPanel("phi:");
        cards.add(calculatePhiPanel, "CALCULATE_PHI");
        calculateDPanel = new EvaluationInteractionPanel("d:");
        cards.add(calculateDPanel, "CALCULATE_D");
        calculateEPanel = new EvaluationInteractionPanel("e:");
        cards.add(calculateEPanel, "CALCULATE_D");
        generateKeysResultsPanel = new ResultsPanel();
        cards.add(generateKeysResultsPanel, "GENERATE_RESULTS");
                
		//Buttons Panel
        lineSeparator = new JSeparator();	    
		
		ActionListener firstButtonActionListener = new ActionListener() {
			public void actionPerformed(ActionEvent e) {
				resetCards();
				if(!algorithm.getCurrentStep().isFirst()){
	    			algorithm.reset();
        			cardsLayout.first(cards);
        		}
				handleButtonsEnable();
			}
		};  

		ActionListener previousButtonActionListener = new ActionListener() {
	    	public void actionPerformed(ActionEvent e) {
	    		resetCards();
	    		if(!algorithm.getCurrentStep().isFirst()){
	    			algorithm.movePreviousStep();
	    			algorithm.undoStep();
        			cardsLayout.previous(cards);
        		}
	    		handleButtonsEnable();
	    	}
	    };

	    ActionListener nextButtonActionListener = new ActionListener() {
        	public void actionPerformed(ActionEvent e) {
        		JPanel card = getCurrentCard();
        		if(card instanceof EvaluationInteractionPanel){
        			((EvaluationInteractionPanel) card).resetToolTip();
        			algorithm.getMessage().setElementToEvaluate(((EvaluationInteractionPanel) card).getOperationValue());
        		}
        	       
        		if(!algorithm.getCurrentStep().isLast()){
        			try{      		
        				algorithm.executeStep();
        				algorithm.moveNextStep();
        				cardsLayout.next(cards);
//        				JPanel card2 = getCurrentCard();
//        				((EvaluationInteractionPanel) card2).getOperationTextField().requestFocusInWindow();
        			}catch(InvalidValueException ex){
        				if(card instanceof EvaluationInteractionPanel){
        					((EvaluationInteractionPanel)card).setToolTip(ex.getMessage());
        				}
        			}
        		}
        		handleButtonsEnable();
        	}	
        };

        ActionListener lastButtonActionListener = new ActionListener() {
			public void actionPerformed(ActionEvent e) {
				if(!algorithm.getCurrentStep().isLast()){
					algorithm.setStrategy(learningStrategy);
					algorithm.executeStep();
					algorithm.resolve();
					algorithm.undoStep();
        			cardsLayout.last(cards);
        			algorithm.setStrategy(evaluationStrategy);
        		}
				handleButtonsEnable();
			}
		};
	  
		buttonsPanel = new ButtonsPanel(modeButtonActionListener, firstButtonActionListener, previousButtonActionListener, nextButtonActionListener, lastButtonActionListener);

		//--------------------------------      
      
		GroupLayout generateKeysTabLayout = new GroupLayout(this);
        this.setLayout(generateKeysTabLayout);
        generateKeysTabLayout.setHorizontalGroup(
            generateKeysTabLayout.createParallelGroup(Alignment.LEADING)
            .addGroup(generateKeysTabLayout.createSequentialGroup()
                .addContainerGap()
                .addGroup(generateKeysTabLayout.createParallelGroup(Alignment.LEADING)
                    .addComponent(lineSeparator, Alignment.TRAILING)
                    .addComponent(progressPanel, GroupLayout.DEFAULT_SIZE, GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
                    .addComponent(cards, GroupLayout.DEFAULT_SIZE, 830, Short.MAX_VALUE)
                    .addComponent(buttonsPanel, GroupLayout.DEFAULT_SIZE, GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
                .addContainerGap())
        );
        generateKeysTabLayout.setVerticalGroup(
            generateKeysTabLayout.createParallelGroup(GroupLayout.Alignment.LEADING)
            .addGroup(generateKeysTabLayout.createSequentialGroup()
                .addContainerGap()
                .addComponent(progressPanel, GroupLayout.PREFERRED_SIZE, 81, GroupLayout.PREFERRED_SIZE)
                .addPreferredGap(ComponentPlacement.RELATED)
                .addComponent(cards, GroupLayout.DEFAULT_SIZE, 159, Short.MAX_VALUE)
                .addPreferredGap(ComponentPlacement.RELATED)
                .addComponent(lineSeparator, GroupLayout.PREFERRED_SIZE, GroupLayout.DEFAULT_SIZE, GroupLayout.PREFERRED_SIZE)
                .addPreferredGap(ComponentPlacement.UNRELATED)
                .addComponent(buttonsPanel, GroupLayout.PREFERRED_SIZE, GroupLayout.DEFAULT_SIZE, GroupLayout.PREFERRED_SIZE)
                .addGap(6, 6, 6))
        );
	}

	public void update(Observable o, Object arg) {
		RSAKeyGenerationMessage message = (RSAKeyGenerationMessage) o;
		this.progressPanel.setPValue(message.getP());
		this.progressPanel.setQValue(message.getQ());
		this.progressPanel.setNValue(message.getN());
		this.progressPanel.setPhiValue(message.getPhi());
		this.progressPanel.setDValue(message.getD());
		this.progressPanel.setEValue(message.getE());
		
		generatePPanel.setOperationValue(message.getP());
        generateQPanel.setOperationValue(message.getQ());
        calculateNPanel.setOperationValue(message.getN());
        calculatePhiPanel.setOperationValue(message.getPhi());
        calculateDPanel.setOperationValue(message.getD());
        calculateEPanel.setOperationValue(message.getE());
        
        generateKeysResultsPanel.setResultsValues("Clave Pública = (e, n)", "Clave Privada = (d, n)", String.format("(%s, %s)", message.getE(), message.getN()), String.format("(%s, %s)", message.getD(), message.getN()));
	}
	
	public void handleButtonsEnable(){
		this.buttonsPanel.getFirstButton().setEnabled(!algorithm.getCurrentStep().isFirst());
		this.buttonsPanel.getPreviousButton().setEnabled(!algorithm.getCurrentStep().isFirst());
		this.buttonsPanel.getNextButton().setEnabled(!algorithm.getCurrentStep().isLast());
		this.buttonsPanel.getLastButton().setEnabled(!algorithm.getCurrentStep().isLast());
	}
	
	private JPanel getCurrentCard(){ 
		JPanel currentCard = null;
		for (Component component : cards.getComponents()) {
			if (component.isVisible()) {
				currentCard = (JPanel) component;
				break;
			}
		}
		return currentCard;
	}
	

	private void resetCards() {
		JPanel card = getCurrentCard();
		if(card instanceof EvaluationInteractionPanel){
			((EvaluationInteractionPanel) card).setOperationValue("");
			((EvaluationInteractionPanel) card).resetToolTip();
		}
	}
	
}	

